const path = require('path');
const { throttle } = require('throttle-debounce');
const Store = require('electron-store');
const FileTransferService = require('./fileTransferService');
const FileInfo = require('./fileInfo');
const TaskStatus = require('./taskStatus');
const configManager = require('./config');
const getUserDataPath = require('./getUserDataPath');
const fs = require('fs');
const log = require('electron-log');

const store = new Store({
    name: 'sync',
    cwd: getUserDataPath(),
});

log.info('引入service');

const COMMON_EVENT = {
    TaskCountChange: 'TASK_COUNT_CHANGE',
    TransferFinished: 'TRANSFER_FINISHED',
};

const DOWNLOAD_EVENT = {
    DownloadTaskAdded: 'DOWNLOAD_TASK_ADDED',
    DownloadTaskStateChange: 'DOWNLOAD_TASK_STATE_CHANGE',
    DownloadProgress: 'DOWNLOAD_PROGRESS',
    DownloadError: 'DOWNLOAD_ERROR',
};

const UPLOAD_EVENT = {
    UploadTaskAdded: 'UPLOAD_TASK_ADDED',
    UploadTaskStateChange: 'UPLOAD_TASK_STATE_CHANGE',
    UploadProgress: 'UPLOAD_PROGRESS',
    UploadError: 'UPLOAD_ERROR',
};

const getDefaultDownloadPath = () => {
    return configManager.getLocal().folderPath;
};

const getProjectId = (filePath) => {
    let projectname = path.relative(
        configManager.getLocal().folderPath,
        filePath
    );

    let names = path.dirname(projectname);

    let logs = names.replace(/\\/g, '/');

    let name = logs.split('/')[0];
    const projectObj = store.get('downProject', {});

    log.info('projectName' + name);
    log.info({ projectObj });
    log.info('busid:' + projectObj[name].busId);

    if (projectObj[name]) {
        return projectObj[name].busId;
    } else {
        return null;
    }
};

/*
 * @Description: 文件传输助手
 */
class FileTransferHelper {
    constructor() {
        // store.set('downloadTaskList', []);
        this.cachedTaskCountPayload = {
            activeDownload: 0,
            runningDownload: 0,
            activeUpload: 0,
            runningUpload: 0,
        };
        this.downloadSession = {
            finished: 0,
            active: false,
            total: 0,
            tasks: [],
        };
        this.uploadSession = {
            finished: 0,
            active: false,
            total: 0,
            tasks: [],
        };
        this.downloadEventHandler = {
            OnNotify: (notify, task) => {
                // 通知
                // console.log(
                //   DOWNLOAD_EVENT.DownloadTaskStateChange,
                //   serializeDownloadTask(task),
                //   notify
                // );
                try {
                    switch (notify) {
                        case TaskStatus.WAIT:
                            {
                                this.downloadSession.active = true;
                                this.downloadSession.total += 1;
                                // transferManager.tryStartPowerSaveBlocker();
                                this.checkActiveTaskCount();
                                break;
                            }
                        case TaskStatus.RUNNING:
                            {
                                this.checkRunningTaskCount();
                                break;
                            }
                        case TaskStatus.FAILED:
                            {
                                this.checkRunningTaskCount();
                                this.checkDownloadSession(task);
                                this.updateDownloadTask(task);
                                break;
                            }
                        case TaskStatus.SUCCESS:
                            {
                                this.downloadSession.finished++;
                                this.checkRunningTaskCount();
                                this.checkActiveTaskCount();
                                this.checkDownloadSession(task);
                                this.updateDownloadTask(task);
                                break;
                            }
                        default:
                            break;
                    }
                } catch (error) {}
            },
            OnProgress: (task) => {
                // console.log(serializeDownloadTask(task));
            },
        };
        this.uploadEventHandler = {
            OnNotify: (notify, task) => {
                // 通知
                // console.log(
                //   UPLOAD_EVENT.UploadTaskStateChange,
                //   serializeUploadTask(task),
                //   notify
                // );
                try {
                    switch (notify) {
                        case TaskStatus.WAIT:
                            {
                                this.uploadSession.total += 1;
                                this.uploadSession.active = true;
                                // transferManager.tryStartPowerSaveBlocker();
                                this.checkActiveTaskCount();
                                break;
                            }
                        case TaskStatus.UPDATE:
                            {
                                this.uploadSession.finished--;
                                this.uploadSession.active = true;
                                // transferManager.tryStartPowerSaveBlocker();
                                this.checkActiveTaskCount();
                                break;
                            }
                        case TaskStatus.RUNNING:
                            {
                                this.checkRunningTaskCount();
                                break;
                            }
                        case TaskStatus.FAILED:
                            {
                                this.checkRunningTaskCount();
                                this.checkUploadSession(task);
                                this.updateUploadTask(task);
                                break;
                            }
                        case TaskStatus.SUCCESS:
                            {
                                this.uploadSession.finished++;
                                this.checkRunningTaskCount();
                                this.checkActiveTaskCount();
                                this.checkUploadSession(task);
                                this.updateUploadTask(task);
                                break;
                            }
                        default:
                            break;
                    }
                } catch (error) {}
            },
            OnProgress: (task) => {
                // console.log(serializeDownloadTask(task));
            },
        };
        this.checkActiveTaskCount = throttle(400, () => {
            console.log('checkActiveTaskCount');
            const payload = {
                ...this.cachedTaskCountPayload,
                activeDownload: this.service.GetDownloadTaskList(3 /* ACTIVE */ ).length,
                activeUpload: this.service.GetUploadTaskList(23 /* ACTIVE */ ).length,
            };
            if (!taskCountIsIdentical(payload, this.cachedTaskCountPayload)) {
                this.cachedTaskCountPayload = payload;
                // console.log(COMMON_EVENT.TaskCountChange, payload);
            }
        });
        this.checkRunningTaskCount = throttle(400, () => {
            console.log('checkRunningTaskCount');
            const payload = {
                ...this.cachedTaskCountPayload,
                runningDownload: this.service.GetDownloadTaskList(2 /* RUNNING */ )
                    .length,
                runningUpload: this.service.GetUploadTaskList(2 /* RUNNING */ ).length,
            };
            if (!taskCountIsIdentical(payload, this.cachedTaskCountPayload)) {
                this.cachedTaskCountPayload = payload;
                // console.log(COMMON_EVENT.TaskCountChange, payload);
            }
        });
        this.checkDownloadSession = (task, { silent = false } = {}) => {
            console.log('checkDownloadSession');
            this.downloadSession.tasks.push(serializeDownloadTask(task));
            const aliveTaskCount = this.service.GetDownloadTaskList(
                2 /* RUNNING */ | 1 /* WAIT */
            ).length;
            if (aliveTaskCount !== 0) return;
            const succeed = this.downloadSession.finished;
            const failed = this.service.GetDownloadTaskList(4 /* FAILED */ ).length;
            // 没有可传输的任务，或者任务没有全部传输完毕时，不发通知
            if (
                this.downloadSession.active === false ||
                this.downloadSession.total > failed + succeed
            )
                return;
            this.downloadSession.total = 0;
            this.downloadSession.finished = 0;
            this.downloadSession.active = false;
            if (failed === 0 && succeed === 0) return;
            if (silent) return;
            const payload = {
                type: 'download',
                totalTaskCount: failed + succeed,
                succeedTaskCount: succeed,
                failedTaskCount: failed,
                lastTask: serializeDownloadTask(task),
                tasks: this.downloadSession.tasks,
                state: getStateForTransferFinished({ succeed, failed }),
            };
            log.info('文件下载成功');

            // const downloadTaskList = store.get('downloadTaskList', []);
            // const list = downloadTaskList.map((td) => {
            //     return {
            //         ...td,
            //         state: td.state == 'FAILED' || td.state == 'SUCCESSED' ?
            //             td.state : 'SUCCESSED',
            //     };
            // });
            // this.downloadPort.postMessage({
            //     type: 'file:download:success',
            //     payload,
            // });
            this.handleTransferFinished(payload);
        };
        this.checkUploadSession = (task, { silent = false } = {}) => {
            console.log('checkUploadSession');
            this.uploadSession.tasks.push(serializeUploadTask(task));

            const aliveTaskCount = this.service.GetUploadTaskList(
                2 /* RUNNING */ | 1 /* WAIT */
            ).length;
            console.log('total', this.uploadSession.total);
            console.log('aliveTaskCount', aliveTaskCount);
            console.log('finished', this.uploadSession.finished);
            console.log(
                'failed',
                this.service.GetUploadTaskList(4 /* FAILED */ ).length
            );

            console.log('更新1');

            if (aliveTaskCount !== 0) return;
            console.log('更新2');

            const succeed = this.uploadSession.finished;
            const failed = this.service.GetUploadTaskList(4 /* FAILED */ ).length;
            if (
                this.uploadSession.active === false ||
                this.uploadSession.total > failed + succeed
            )
                return;
            console.log('更新3');

            this.uploadSession.total = 0;
            this.uploadSession.finished = 0;
            this.uploadSession.active = false;
            if (failed === 0 && succeed === 0) return;
            console.log('更新4');

            if (silent) return;
            console.log('更新5');

            const payload = {
                type: 'upload',
                totalTaskCount: failed + succeed,
                succeedTaskCount: succeed,
                failedTaskCount: failed,
                total: this.uploadSession.tasks.length,
                // pausedTaskCount: paused,
                lastTask: serializeUploadTask(task),
                state: getStateForTransferFinished({ succeed, failed }),
            };
            log.info('文件上传成功');
            this.uploadPort.postMessage({
                type: 'file:upload:success',
                payload,
            });
            this.handleTransferFinished(payload);
        };
    }

    get service() {
        return this._service;
    }

    createServiceInstance({ downloadPort, uploadPort }) {
        const service = new FileTransferService();
        this._service = service;
        this.downloadPort = downloadPort;
        this.uploadPort = uploadPort;
        if (downloadPort) {
            service.InitializeDownload(downloadPort);
        } else {
            service.InitializeUpload(uploadPort);
        }
    }

    async createDownloadTask(params) {
        const { items } = params;
        const op = (item) => {
            return new Promise((resolve) => {
                setTimeout(() => {
                    const { taskId, busId, pjId, coId, pjName } = item;
                    const task = this.service.CreateDownloadTask();
                    task.SetStatus(TaskStatus.WAIT);
                    task.SetID(taskId);
                    task.SetBusId(busId);
                    task.SetPjId(pjId);
                    task.SetCoId(coId);
                    task.SetPjName(pjName);

                    resolve(task);
                }, 0);
            });
        };
        const tasks = [];
        for (const item of items) {
            // eslint-disable-next-line no-await-in-loop
            const task = await op(item);
            tasks.push(task);
        }
    }

    async createDownloadTaskRe(params) {
        const { items } = params;
        const op = (item) => {
            return new Promise((resolve) => {
                setTimeout(() => {
                    const {
                        localPath,
                        fileId,
                        size,
                        name,
                        type = 'file',
                        url,
                        nodeId,
                        extraData,
                    } = item;
                    const file = new FileInfo();
                    file.SetID(fileId);
                    file.SetNodeId(nodeId);
                    if (name !== undefined) file.SetName(name);
                    if (size !== undefined) file.SetSize(size);
                    file.SetType(type);
                    const task = this.service.CreateDownloadTask();
                    task.SetStatus(TaskStatus.WAIT);
                    task.SetFileInfo(file);
                    task.SetUrl(url);
                    task.SetLocalPath(localPath);
                    task.SubscribeEvent(this.downloadEventHandler);
                    task.SetExtraData(extraData);
                    const serialized = serializeDownloadTask(task);
                    resolve(serialized);
                }, 0);
            });
        };
        const tasks = [];
        for (const item of items) {
            // eslint-disable-next-line no-await-in-loop
            const task = await op(item);
            tasks.push(task);
        }
        // 通知
        // console.log(DOWNLOAD_EVENT.DownloadTaskAdded, tasks);
        this.saveDownloadTaskSession(tasks);
    }

    async createUploadTask(params) {
        const { items } = params;

        console.log('创建上传任务');
        const op = (item) => {
            return new Promise((resolve, reject) => {
                setTimeout(() => {
                    const { taskId, coId } = item;
                    const task = this.service.CreateUploadTask();
                    task.SetID(taskId);
                    task.SetCoId(coId);
                    resolve(task);
                }, 0);
            });
        };
        const tasks = [];
        for (const item of items) {
            try {
                // eslint-disable-next-line no-await-in-loop

                const task = await op(item);
                tasks.push(task);
            } catch {
                continue;
            }
        }
        // 通知
        // console.log(DOWNLOAD_EVENT.DownloadTaskAdded, tasks);
        // this.saveUploadTaskSession(tasks);
    }

    handleTransferFinished = (payload) => {
        this.afterTransferFinished();
    };

    afterTransferFinished() {
        // transferManager.handleAfterTransferFinished();
    }

    /**
     * 恢复所有下载未完成任务
     * @returns
     */
    resumeAllDownloadTask() {
        const downloadTaskList = store.get('downloadTaskList', []);
        const result = downloadTaskList.filter((x) => x.state !== 'SUCCESSED');
        this.createDownloadTaskRe({
            items: result.map((x) => {
                return {
                    localPath: x.localPath,
                    fileId: x.file.id,
                    size: x.file.size,
                    name: x.file.name,
                    type: 'file',
                    url: x.url,
                    extraData: x.extraData,
                    // extraData: x.extraData,
                };
            }),
        });
    }

    /**
     * 恢复所有上传未完成任务
     * @returns
     */
    resumeAllUploadTask() {
        const uploadTaskList = store.get('uploadTaskList', []);
        const result = uploadTaskList.filter((x) => x.state !== 'SUCCESSED');
        console.log({ result });
    }

    /**
     * 将该批次上传任务存入数据库
     * @param tasks
     */
    saveUploadTaskSession(tasks) {
        const uploadTaskList = store.get('uploadTaskList', []);
        const result = uploadTaskList.map((x) => {
            return Object.assign(
                x,
                tasks.find((y) => y.file.id === x.file.id)
            );
        });

        const idlist = result.map((td) => td.file.id);
        const arr = tasks.filter((td) => idlist.indexOf(td.file.id) == -1);
        store.set('uploadTaskList', [...result, ...arr]);
    }

    /**
     * 更新数据库上传任务状态
     */
    updateUploadTask(task) {
        const uploadTaskList = store.get('uploadTaskList', []);
        const serialize = serializeUploadTask(task);
        let result = [];

        if (serialize.state == 'FAILED') {
            // 失败的任务移除上传队列
            result = uploadTaskList.filter((td) => td.taskId != serialize.taskId);
        } else {
            result = uploadTaskList.map((x) => {
                return Object.assign(
                    x, [serialize].find((y) => y.taskId === x.taskId)
                );
            });
        }
        store.set('uploadTaskList', result);
    }

    /**
     * 将该批次下载任务存入数据库
     * @param tasks
     */
    saveDownloadTaskSession(tasks) {
        const downloadTaskList = store.get('downloadTasks', []);
        const result = downloadTaskList.map((x) => {
            return Object.assign(
                x,
                tasks.find((y) => y.id === x.id)
            );
        });
        const idlist = result.map((td) => td.id);
        const arr = tasks.filter((td) => idlist.indexOf(td.id) == -1);
        store.set('downloadTasks', [...result, ...arr]);
    }

    /**
     * 更新数据库下载任务状态
     */
    updateDownloadTask(task) {
        const downloadTaskList = store.get('downloadTasks', []);
        const serialize = serializeDownloadTask(task);
        const result = downloadTaskList.map((x) => {
            return Object.assign(
                x, [serialize].find((y) => y.id === x.id)
            );
        });
        store.set('downloadTasks', result);
    }

    /**
     * 获取数据库下载任务
     */
    getDownloadTask() {
        return store.get('downloadTaskList');
    }

    /**
     * 获取数据库上传任务
     */
    getDownloadTask() {
        return store.get('uploadTaskList', []);
    }
}

const fileTransferHelper = new FileTransferHelper();

// transferManager.setSDK(fileTransferHelper);

module.exports = fileTransferHelper;

const statusMap = {
    [1 /* WAIT */ ]: 'QUEUED',
    [2 /* RUNNING */ ]: 'IN_PROGRESS',
    [4 /* FAILED */ ]: 'FAILED',
    [8 /* SUCCESS */ ]: 'SUCCESSED',
};

const serializeFileInfo = (info) => {
    return {
        nodeId: info.GetNodeId(),
        id: info.GetID(),
        parentId: info.GetParentID(),
        name: info.GetName(),
        type: info.GetType(),
        size: info.GetSize(),
        createTime: info.GetCreateTime(),
        modifyTime: info.GetModifyTime(),
    };
};

const serializeDownloadTask = (task) => {
    const fileInfo = task.GetFileInfo();
    return {
        taskId: task.GetID(),
        url: task.GetUrl(),
        file: serializeFileInfo(fileInfo),
        progress: {
            percentage: task.GetPercentage(),
            totalByte: fileInfo.GetSize(),
            loadedByte: task.GetPos(),
            timestamp: Date.now(),
        },
        state: statusMap[task.GetStatus()],
        localPath: task.GetLocalPath(),
        taskType: 'download',
        createTime: task.GetCreateTime(),
        endTime: task.GetEndTime(),
        extraData: task.GetExtraData(),
    };
};

const serializeUploadTask = (task) => {
    const fileInfo = task.GetFileInfo();
    console.log('更新6');

    return {
        taskId: task.GetID(),
        file: serializeFileInfo(fileInfo),
        parentFolderId: fileInfo.GetParentID(),
        parentTaskId: task.GetParentTaskID(),
        progress: {
            totalByte: fileInfo.GetSize(),
            loadedByte: task.GetPos(),
            timestamp: Date.now(),
        },
        state: statusMap[task.GetStatus()],
        localPath: path.join(task.GetLocalPath()),
        taskType: 'upload',
        createTime: task.GetCreateTime(),
        endTime: task.GetEndTime(),
        extraData: task.GetExtraData(),
    };
};

const taskCountIsIdentical = (a, b) => {
    const keys = [
        'activeDownload',
        'activeUpload',
        'runningDownload',
        'runningUpload',
    ];
    return keys.every((key) => a[key] === b[key]);
};

const getStateForTransferFinished = (params) => {
    const { succeed, failed } = params;
    // 全成功为「成功」
    if (failed === 0 && succeed > 0) return 'succeed';
    // 全失败为「失败」
    if (failed !== 0) return 'failed';
    // 部分「失败」，部分「暂停」的，为「部分完成」
    return 'partial';
};
